#ifndef __BLOCKQUEUE_HPP__
#define __BLOCKQUEUE_HPP__
#include <iostream>
#include <queue>
#include <pthread.h>
#include <semaphore.h>

template<typename T>
class RingQueue
{
public:
    RingQueue(int capacity = 10): _ring_queue(capacity), _capacity(capacity)
    {
        sem_init(&_room_sem, 0, capacity);
        sem_init(&_data_sem, 0, 0);
        pthread_mutex_init(&_producer_mutex, nullptr);
        pthread_mutex_init(&_consumer_mutex, nullptr);
    }

    void push(T& in)    // for producer
    {
        sem_wait(&_room_sem);
        pthread_mutex_lock(&_producer_mutex);

        _ring_queue.at(_producer_index++) = in;
        _producer_index %= _capacity;       // avoid index overflow

        pthread_mutex_unlock(&_producer_mutex);
        sem_post(&_data_sem);
    }

    void pop(T* out)    // for consumer
    {
        sem_wait(&_data_sem);
        pthread_mutex_lock(&_consumer_mutex);

        *out = _ring_queue.at(_consumer_index++);
        _consumer_index %= _capacity;       // avoid index overflow

        pthread_mutex_unlock(&_consumer_mutex);
        sem_post(&_room_sem);
    }

    ~RingQueue()
    {
        sem_destroy(&_room_sem);
        sem_destroy(&_data_sem);
        pthread_mutex_destroy(&_producer_mutex);
        pthread_mutex_destroy(&_consumer_mutex);
    }
private:

private:
    std::vector<T> _ring_queue;
    int _capacity;
    int _producer_index = 0;
    int _consumer_index = 0;
    sem_t _room_sem;
    sem_t _data_sem;
    pthread_mutex_t _producer_mutex;
    pthread_mutex_t _consumer_mutex;
};


#endif